package com.bosch.in.rtp1.ept.util;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;

import com.bosch.in.rtp1.ept.constant.CommonConstants;
import com.bosch.in.rtp1.ept.constant.FXFactorConstant;
import com.bosch.in.rtp1.ept.constant.FinishRecipeConstant;
import com.bosch.in.rtp1.ept.constant.HCFilterColumnName;
import com.bosch.in.rtp1.ept.constant.HoldCodeType;
import com.bosch.in.rtp1.ept.constant.LotStatusColumnName;
import com.bosch.in.rtp1.ept.constant.ModifiedLinksContanst;
import com.bosch.in.rtp1.ept.constant.ReleasedHoldColumnName;
import com.bosch.in.rtp1.ept.constant.ReleasedHoldParameter;
import com.bosch.in.rtp1.ept.constant.SkippedRecipeConstanst;
import com.bosch.in.rtp1.ept.model.dto.FXFactor;
import com.bosch.in.rtp1.ept.model.dto.FshRcpRecord;
import com.bosch.in.rtp1.ept.model.dto.HoldCodeFilter;
import com.bosch.in.rtp1.ept.model.dto.Link;
import com.bosch.in.rtp1.ept.model.dto.LotStatus;
import com.bosch.in.rtp1.ept.model.dto.ReleasedHold;
import com.bosch.in.rtp1.ept.model.dto.SkippedFshRcpCon;
import com.bosch.in.rtp1.ept.model.technical.Condition4FxFactor;

/**
 * Utility for converting entity object into JSON class which is 
 * supported by Jqgrid or vice versa, data posted from Jqgrid 
 * can be transformed into entity object.
 * @author TPR1HC
 *
 */
public class ConverterUtility {
	
	/**
     * ID attribute required for Jqgrid data.
     */
    private static final String JGGRID_ID = "id";
    
    /**
     * CELL attribute required for Jqgrid data.
     */
    private static final String JGGRID_CELL = "cell";
	
    private static final String QUOTE = "'";
    private static final String COMMAS = ",";
    private static final String OPEN = "{";
    private static final String CLOSE = "}";
    private static final String OPEN_BRACKET = "[";
    private static final String CLOSE_BRACKET = "]";
    private static final String COLON = ":";
    
    private static final String FILE_PROTOCAL = "file:";
    private static final String FILE_PROTOCAL_SEPARATOR = "///";
    private static final String PATH_NET_WINDOW_SEPARATOR = "\\\\";
    private static final String FILE_SEPARATOR_PATTERN = "//{1}";
    private static final String PATH_UNIX_SEPARATOR = "/";
    private static final String PATH_WINDOW_SEPARATOR = "\\";
    
	/**
	 * Transform returned LotStatus list into JSON format which is supported by Jqgrid
	 * @param lotStatuses	LotStatus list
	 */
	public static JSONArray convertToJSONRows(List<LotStatus> lotStatuses) {
		// Init cell array attribute
		JSONArray cellarray = new JSONArray();
		JSONObject cellobj = new JSONObject();
		JSONArray cell = new JSONArray();
	
		//DecimalFormat df = new DecimalFormat(CommonConstants.NUMBER_FORMAT);
		// convert from string to date
	    SimpleDateFormat sdf = new SimpleDateFormat(CommonConstants.FULL_DATE_FORMAT);
	    
		for (LotStatus lotStatus : lotStatuses) {
			// fill in cell attribute by LotStatus data
			cellobj.put(JGGRID_ID, String.valueOf(lotStatus.getLotId()));
			cell.add(lotStatus.getLotId());
			cell.add(lotStatus.getCurMainQTY());
			cell.add(lotStatus.getPartId());
			cell.add(lotStatus.getLotType());
			cell.add(lotStatus.getCustomerName());
			cell.add(lotStatus.getPriority());
			cell.add(lotStatus.getState());
			cell.add(lotStatus.getWaitTime());
			cell.add(lotStatus.getX_plan());
			cell.add(lotStatus.getX_rw());
			cell.add(lotStatus.getX_rwh());
			cell.add(lotStatus.getLotComment());
			cell.add(lotStatus.getHoldCode());
			cell.add(lotStatus.getHoldReason());
			cell.add(lotStatus.getProdArea());
			cell.add(lotStatus.getLocation());
			cell.add(lotStatus.getCurPrcdId());
			cell.add(lotStatus.getStage());
			cell.add(lotStatus.getRecipeId());
			cell.add(lotStatus.getRecipeTitle());
			cell.add(lotStatus.getEqpType());
			cell.add(lotStatus.getEquipID());
			cell.add(sdf.format(lotStatus.getQueueTime()));
			cell.add(sdf.format(lotStatus.getStartTime()));
			cell.add(lotStatus.getCr1Sap());
			
			// JSON cell object format: {cell: [data1, data2, ...]} 
			cellobj.put(JGGRID_CELL, cell);
			cell.clear();
			cellarray.add(cellobj);
		}
		return cellarray;
	}
	
	/**
	 * Transform HoldCodeFilter list into JSON format which is supported by Jqgrid
	 * @param holdCodeFilters	HoldCodeFilter list
	 */
	public static JSONArray convertToHCF_JSONRows(List<HoldCodeFilter> holdCodeFilters) {
		// Init cell array attribute
		JSONArray cellarray = new JSONArray();
		JSONObject cellobj = new JSONObject();
		JSONArray cell = new JSONArray();
	
		for (HoldCodeFilter holdCodeFilter : holdCodeFilters) {
			// fill in cell attribute by LotStatus data
			cellobj.put(JGGRID_ID, String.valueOf(holdCodeFilter.getId()));
			cell.add(holdCodeFilter.getId());
			cell.add(holdCodeFilter.getComment());
			List<String> hcFilterValues = holdCodeFilter.getPatterns();
			// list all HC pattern which may from 'HC1' up to maximum 'HC20'
			for (int i = 0; i < CommonConstants.HC_TOTAL; i++) {
				if (i < hcFilterValues.size()) {
					cell.add(hcFilterValues.get(i));
				} else {
					cell.add("");
				}
			}
			cell.add(holdCodeFilter.getOrder());
			
			// JSON cell object format: {cell: [data1, data2, ...]} 
			cellobj.put(JGGRID_CELL, cell);
			cell.clear();
			cellarray.add(cellobj);
		}
		return cellarray;
	}
	
	/**
	 * Transform HoldCodeFilter list into JSON format which is supported by Jqgrid
	 * @param holdCodeFilters	HoldCodeFilter list
	 */
	public static String convertToJSONArray(List<HoldCodeFilter> holdCodeFilters) {
		StringBuffer strBuffer = new StringBuffer();
		// start JSON array
		strBuffer.append(OPEN_BRACKET);
		for (HoldCodeFilter holdCodeFilter : holdCodeFilters) {
			// insert row data
			strBuffer.append(OPEN);
			strBuffer.append(HCFilterColumnName.ID + COLON + QUOTE + holdCodeFilter.getId() + QUOTE);
			strBuffer.append(COMMAS);
			strBuffer.append(HCFilterColumnName.COMMENT + COLON + QUOTE + holdCodeFilter.getComment() + QUOTE);
			List<String> hcFilterValues = holdCodeFilter.getPatterns();
			// list all HC pattern which may from 'HC1' up to maximum 'HC20'
			for (int i = 0; i < hcFilterValues.size(); i++) {
				strBuffer.append(COMMAS);
				strBuffer.append(HCFilterColumnName.HC + String.valueOf( i + 1) + COLON + QUOTE + hcFilterValues.get(i) + QUOTE);
			}
			strBuffer.append(COMMAS);
			strBuffer.append(HCFilterColumnName.ORDER + COLON + QUOTE + holdCodeFilter.getOrder() + QUOTE);
			strBuffer.append(CLOSE);
			strBuffer.append(COMMAS);
		}
		// delete last commas
		if (strBuffer.length() > 1) {
			strBuffer.delete(strBuffer.length() - 1, strBuffer.length());
		}
		// end JSON array
		strBuffer.append(CLOSE_BRACKET);
		return strBuffer.toString();
	}
	
	/**
	 * Get request parameters from Jqgrid request to build HoldCodeFilter 
	 * entity object. 
	 * @param request Http request
	 * @return HoldCodeFilter object
	 */
	public static HoldCodeFilter convertToHCFilter(HttpServletRequest request) {
		HoldCodeFilter hcFilter = new HoldCodeFilter();
		String id = request.getParameter(HCFilterColumnName.ID.toLowerCase());
		if (id != null && !id.equals("")) {
			// in case user create new HoldCodeFilter, the id will be not available.
			// Then casting string to numeric will generate exception, but this is not the error for creating new.
			try {
				hcFilter.setId(Integer.parseInt(id));
			} catch (Exception e) {}
			hcFilter.setComment(request.getParameter(HCFilterColumnName.COMMENT));
			List<String> patterns = new ArrayList<String>();
			for (int i = 1; i <= CommonConstants.HC_TOTAL; i++) {
				String hc = request.getParameter(HCFilterColumnName.HC + i);
				if (hc != null && !hc.equals("")) {
					patterns.add(hc);
				}
			}
			hcFilter.setPatterns(patterns);
			
			// set type for HoldCode or HoldCode Reclassified
			if (request.getParameter(CommonConstants.COMMAND).equals(CommonConstants.HC_FILTER)) {
				hcFilter.setType(HoldCodeType.HC);
			} else if (request.getParameter(CommonConstants.COMMAND).equals(CommonConstants.HC_RECLASS)){
				hcFilter.setType(HoldCodeType.HC_RECLASF);
			} else {
				hcFilter.setType(HoldCodeType.HC_ACTIVE);
			}
			
			try {
				hcFilter.setOrder(Integer.parseInt(request.getParameter(HCFilterColumnName.ORDER)));
			} catch (Exception e) {
				//hcFilter.setOrder(10);
			}
		}
		return hcFilter;
	}
	
	/**
	 * Transform returned FXFactor list into JSON format which is supported by Jqgrid
	 * @param fxFactors	FXFactor list
	 */
	public static JSONArray convertToRows(List<FXFactor> fxFactors) {
		// Init cell array attribute
		JSONArray cellarray = new JSONArray();
		JSONObject cellobj = new JSONObject();
		JSONArray cell = new JSONArray();
	
		for (FXFactor fxFactor : fxFactors) {
			// fill in cell attribute by LotStatus data
			cellobj.put(ConverterUtility.JGGRID_ID, fxFactor.getMonth());
			cell.add(FXFactorConstant.MONTH_LIST[fxFactor.getMonth()-1]);
			cell.add(fxFactor.getFxPlan() == FXFactorConstant.EMPTY_FX_FACTOR ? "": fxFactor.getFxPlan());
			cell.add(fxFactor.getFxRW() == FXFactorConstant.EMPTY_FX_FACTOR ? "": fxFactor.getFxRW());
			cell.add(fxFactor.getFxRWH() == FXFactorConstant.EMPTY_FX_FACTOR ? "": fxFactor.getFxRWH());
			cell.add(fxFactor.getComment());
			cell.add(fxFactor.getStatus());
			cell.add(fxFactor.getYear());
			cell.add(fxFactor.getProdArea());
			cell.add(fxFactor.getPriority());
			// JSON cell object format: {cell: [data1, data2, ...]} 
			cellobj.put(ConverterUtility.JGGRID_CELL, cell);			
			cellarray.add(cellobj);
			cell.clear();
		}
		return cellarray;
	}
	
	/**
	 * Transform returned Link list into JSON format which is supported by Jqgrid
	 * @param Link list
	 */
	public static JSONArray convertLinksToRows(List<Link> links) {
		// Init cell array attribute
		JSONArray cellarray = new JSONArray();
		JSONObject cellobj = new JSONObject();
		JSONArray cell = new JSONArray();
	
		for (Link link : links) {
			// fill in cell attribute by LotStatus data
			cellobj.put(ConverterUtility.JGGRID_ID, link.getId());
			cell.add(link.getId());
			cell.add(link.getTitle());
			cell.add(link.getLink());
			cell.add(link.getOrder());
			cell.add(link.getStatus());
			// JSON cell object format: {cell: [data1, data2, ...]} 
			cellobj.put(ConverterUtility.JGGRID_CELL, cell);			
			cellarray.add(cellobj);
			cell.clear();
		}
		return cellarray;
	}
	
	/**
	 * Transform returned FshRcpRecord list into JSON format which is supported by Jqgrid
	 * @param fxFactors	FXFactor list
	 */
	public static JSONArray convertFshRcpRecordsToRows(List<FshRcpRecord> fshRcpRecords, int type) {
		// Init cell array attribute
		JSONArray cellarray = new JSONArray();
		JSONObject cellobj = new JSONObject();
		JSONArray cell = new JSONArray();
		SimpleDateFormat sdf = new SimpleDateFormat(CommonConstants.FULL_DATE_FORMAT);
		for (FshRcpRecord fshRcpRecord : fshRcpRecords) {
			// fill in cell attribute by LotStatus data
			cellobj.put(ConverterUtility.JGGRID_ID, fshRcpRecords.indexOf(fshRcpRecord));
			cell.add(fshRcpRecord.getLotId());
			cell.add(fshRcpRecord.getPartId());
			cell.add(fshRcpRecord.getFactorValue());
			if (FinishRecipeConstant.X_PLAN != type)
			{
				cell.add(sdf.format(fshRcpRecord.getTimeRev()));
			}
			cell.add(fshRcpRecord.getRecipe());
			if (FinishRecipeConstant.X_RWH == type)
			{
				cell.add(fshRcpRecord.getHoldDuration());
				cell.add(fshRcpRecord.getHoldCode());
				cell.add(fshRcpRecord.getHoldComment());
				cell.add(fshRcpRecord.getReleaseComment());
				cell.add(fshRcpRecord.getCustomerName());
			}
			cell.add(fshRcpRecord.getProdArea());
			cell.add(fshRcpRecord.getLocation());
			cell.add(fshRcpRecord.getStage());
			cell.add(fshRcpRecord.getEqpType());
			// JSON cell object format: {cell: [data1, data2, ...]} 
			cellobj.put(ConverterUtility.JGGRID_CELL, cell);			
			cellarray.add(cellobj);
			cell.clear();
		}
		return cellarray;
	}
	
	/**
	 * Add absent month into returned fxFactor list 
	 * @param fxFactors	FXFactor list
	 */
	
	public static void fillMonth(List<FXFactor> fxFactors,Condition4FxFactor condition4FxFactor)
	{
		int month=1;
		int j=0;
		int curMonth;
		
		while (j<fxFactors.size() && month<FinishRecipeConstant.NUM_OF_TIME_POINT)
		{
			curMonth = fxFactors.get(j).getMonth();
			if (month<curMonth)
			{
				addFxFactor(fxFactors,condition4FxFactor,j,month);
			}
			++j;
			++month;
		}
		if (month<FinishRecipeConstant.NUM_OF_TIME_POINT)
		{
			for (int i=month; i<FinishRecipeConstant.NUM_OF_TIME_POINT;++i)
			{
				addFxFactor(fxFactors,condition4FxFactor,j,i);
				++j;
			}
		}
	}
	
	/**
	 * Get request parameters to build SkippedFshRcpCon 
	 * entity object. 
	 * @param request Http request
	 * @return SkippedFshRcpCon object
	 */
	
	public static SkippedFshRcpCon extractSkpFshRcpCon(HttpServletRequest request)
	{
		SkippedFshRcpCon skippedFshRcpCon = new SkippedFshRcpCon();
		
		String requestValue = request.getParameter(SkippedRecipeConstanst.BASE_LEAD_TIME_VAL_PARAM);
		skippedFshRcpCon.setBaseLeadTimeVal(Float.parseFloat(requestValue));
		
		requestValue = request.getParameter(SkippedRecipeConstanst.BASE_LEAD_TIME_OP_PARAM);
		skippedFshRcpCon.setBaseLeadTimeOp(Integer.parseInt(requestValue));
		
		requestValue = request.getParameter(SkippedRecipeConstanst.RUN_TIME_VAL_PARAM);
		skippedFshRcpCon.setRunTimeVal(Float.parseFloat(requestValue));
		
		requestValue = request.getParameter(SkippedRecipeConstanst.RUN_TIME_OP_PARAM);
		skippedFshRcpCon.setRunTimeOp(Integer.parseInt(requestValue));
		
		requestValue = request.getParameter(SkippedRecipeConstanst.WAIT_TIME_VAL_PARAM);
		skippedFshRcpCon.setWaitTimeVal(Float.parseFloat(requestValue));
		
		requestValue = request.getParameter(SkippedRecipeConstanst.WAIT_TIME_OP_PARAM);
		skippedFshRcpCon.setWaitTimeOp(Integer.parseInt(requestValue));
		
		if (request.getParameter(SkippedRecipeConstanst.APPLY_FOR_ALL) == null)
		{
			skippedFshRcpCon.setApplyForAll(false);
		}
		else
		{
			skippedFshRcpCon.setApplyForAll(true);
		}
		
		//add to parameters list
		skippedFshRcpCon.builParameterValuesList();
		return skippedFshRcpCon;
	}
	
	/**
	 * Get request parameters from Jqgrid request to build FxFactor 
	 * entity object. 
	 * @param request Http request
	 * @return FXFactor object
	 */
	public static List<FXFactor> convertToFXFactors(HttpServletRequest request)  {
		//get JSON array from request
		JSONArray outerArray = (JSONArray) JSONSerializer.toJSON(getJsonDataList(request, FXFactorConstant.FXTABLE));
		JSONObject json = (JSONObject) outerArray.get(0);
		//convert to FX Factor
		return fromJSONtoFXFactor(json);
	}
	
	/**
	 * Get request parameters from Jqgrid request to build Link 
	 * entity object. 
	 * @param request Http request
	 * @return Link object
	 */
	public static List<Link> convertToLinks(HttpServletRequest request)  {
		//get JSON array from request
		JSONArray outerArray = (JSONArray) JSONSerializer.toJSON(getJsonDataList(request, ModifiedLinksContanst.JSON_LINK));
		JSONObject json = (JSONObject) outerArray.get(0);
		//convert to FX Factor
		return fromJSONtoLink(json);
	}
	
	/**
	 * Convert Json String from Jqgrid request to LotStatus list 
	 * @param jsonLotStatuses	json String
	 * @return List<LotStatus>
	 * @throws ParseException 
	 */
	public static List<LotStatus> convertToLotStatuses(String jsonLotStatuses) throws ParseException  {
		//get JSON array from request
		JSONArray jsonObj = (JSONArray) JSONSerializer.toJSON(jsonLotStatuses);
		JSONObject jsonLotSta;
		List<LotStatus> lotStatuses = new ArrayList<LotStatus>();
		
		// convert string to date
		SimpleDateFormat sdf = new SimpleDateFormat(CommonConstants.FULL_DATE_FORMAT);
		
		for (int i = 0; i < jsonObj.size(); i++) {
			jsonLotSta =  (JSONObject) jsonObj.getJSONObject(i);
			
			// index
			int index = 0;
			LotStatus lot = new LotStatus();
			// check if JSON object has attribute 'cell'
			if (jsonLotSta.containsKey(JGGRID_CELL)) { 
				// JSON cell object format: {cell: [data1, data2, ...]} 
				JSONArray jsonArr = jsonLotSta.getJSONArray(JGGRID_CELL);
				// when jqgrid is firstly loaded, json array doesn't have column name
				if (jsonArr != null) {
					lot.setLotId(jsonArr.getString(index++));
					lot.setCurMainQTY(jsonArr.getInt(index++));
					lot.setPartId(jsonArr.getString(index++));
					lot.setLotType(jsonArr.getString(index++));
					lot.setCustomerName(jsonArr.getString(index++));
					lot.setPriority(jsonArr.getInt(index++));
					lot.setState(jsonArr.getString(index++));
					lot.setWaitTime((float) jsonArr.getDouble(index++));
					lot.setX_plan((float) jsonArr.getDouble(index++));
					lot.setX_rw((float) jsonArr.getDouble(index++));
					lot.setX_rwh((float) jsonArr.getDouble(index++));
					lot.setLotComment(jsonArr.getString(index++));
					lot.setHoldCode(jsonArr.getString(index++));
					lot.setHoldReason(jsonArr.getString(index++));
					lot.setProdArea(jsonArr.getString(index++));
					lot.setLocation(jsonArr.getString(index++));
					lot.setCurPrcdId(jsonArr.getString(index++));
					lot.setStage(jsonArr.getString(index++));
					lot.setRecipeId(jsonArr.getString(index++));
					lot.setRecipeTitle(jsonArr.getString(index++));
					lot.setEqpType(jsonArr.getString(index++));
					lot.setEquipID(jsonArr.getString(index++));
					lot.setQueueTime(sdf.parse(jsonArr.getString(index++)));
					lot.setStartTime(sdf.parse(jsonArr.getString(index++)));
					lot.setCr1Sap(jsonArr.getString(index++));
				} 
			} else {
				// JSON object format: {attr1: data1, attr2:data2, ...} 
				lot.setLotId(jsonLotSta.getString(LotStatusColumnName.LOT_ID_DB));
				lot.setCurMainQTY(jsonLotSta.getInt(LotStatusColumnName.CURMAIN_QTY_DB));
				lot.setPartId(jsonLotSta.getString(LotStatusColumnName.PART_ID_DB));
				lot.setLotType(jsonLotSta.getString(LotStatusColumnName.LOT_TYPE_DB));
				lot.setCustomerName(jsonLotSta.getString(LotStatusColumnName.CUSTOMER_NAME_DB));
				lot.setPriority(jsonLotSta.getInt(LotStatusColumnName.PRIORITY_DB));
				lot.setState(jsonLotSta.getString(LotStatusColumnName.STATE_DB));
				lot.setWaitTime((float) jsonLotSta.getDouble(LotStatusColumnName.WAIT_TIME_DB));
				lot.setX_plan((float) jsonLotSta.getDouble(LotStatusColumnName.X_PLAN_DB));
				lot.setX_rw((float) jsonLotSta.getDouble(LotStatusColumnName.X_RW_DB));
				lot.setX_rwh((float) jsonLotSta.getDouble(LotStatusColumnName.X_RWH_DB));
				lot.setLotComment(jsonLotSta.getString(LotStatusColumnName.LOT_COMMENT_DB));
				lot.setHoldCode(jsonLotSta.getString(LotStatusColumnName.HOLD_CODE_DB));
				lot.setHoldReason(jsonLotSta.getString(LotStatusColumnName.HOLD_REASON_DB));
				lot.setProdArea(jsonLotSta.getString(LotStatusColumnName.PRO_AREA_DB));
				lot.setLocation(jsonLotSta.getString(LotStatusColumnName.LOCATION_DB));
				lot.setCurPrcdId(jsonLotSta.getString(LotStatusColumnName.CUR_PRCD_ID_DB));
				lot.setStage(jsonLotSta.getString(LotStatusColumnName.STAGE_DB));
				lot.setRecipeId(jsonLotSta.getString(LotStatusColumnName.RECIPE_ID_DB));
				lot.setRecipeTitle(jsonLotSta.getString(LotStatusColumnName.RECIPE_TITLE_DB));
				lot.setEqpType(jsonLotSta.getString(LotStatusColumnName.EQP_TYPE_DB));
				lot.setEquipID(jsonLotSta.getString(LotStatusColumnName.EQUIP_ID_DB));
				lot.setQueueTime(sdf.parse(jsonLotSta.getString(LotStatusColumnName.QUEUE_TIME_DB)));
				lot.setStartTime(sdf.parse(jsonLotSta.getString(LotStatusColumnName.START_TIME_DB)));
				lot.setCr1Sap(jsonLotSta.getString(LotStatusColumnName.CR1_SAP_DB));
			}
			lotStatuses.add(lot);
		}
			
		return lotStatuses;
	}
	
	/**
	 * Transform returned ReleaseHold list into JSON format which is supported by Jqgrid
	 * @param releaseHolds	ReleaseHold list
	 */
	public static JSONArray convertToRH_JSONRows(List<ReleasedHold> releaseHolds) {
		// Init cell array attribute
		JSONArray cellarray = new JSONArray();
		JSONObject cellobj = new JSONObject();
		JSONArray cell = new JSONArray();
	
		//DecimalFormat df = new DecimalFormat("###.#");
		SimpleDateFormat sdf = new SimpleDateFormat(CommonConstants.FULL_DATE_FORMAT);
		int index = 0;
		for (ReleasedHold releasedHold : releaseHolds) {
			index++;
			// fill in cell attribute by ReleasedHold data
			cellobj.put(JGGRID_ID, String.valueOf(index));
			cell.add(releasedHold.getLotId());
			cell.add(releasedHold.getHeldDuration());
			cell.add(releasedHold.getWaferNumber());
			cell.add(releasedHold.getPartId());
			cell.add(releasedHold.getLotType());
			cell.add(releasedHold.getCustomerName());
			cell.add(releasedHold.getMaxPriority());
			cell.add(releasedHold.getProdArea());
			cell.add(releasedHold.getLocation());
			cell.add(releasedHold.getStage());
			cell.add(releasedHold.getRecipeId());
			cell.add(releasedHold.getRecipeTitle());
			cell.add(sdf.format(releasedHold.getHaltStart()));
			cell.add(sdf.format(releasedHold.getHaltEnd()));
			cell.add(releasedHold.getHoldCode());
			cell.add(releasedHold.getHoldComment());
			cell.add(releasedHold.getReleasedComment());
			cell.add(releasedHold.getHoldCodeReclassified());
			cell.add(releasedHold.getHoldCommentReclassified());
			// JSON cell object format: {cell: [data1, data2, ...]} 
			cellobj.put(JGGRID_CELL, cell);
			cell.clear();
			cellarray.add(cellobj);
		}
		return cellarray;
	}
	
	/**
	 * This method convert from BigDecimal value to Float value
	 * @param value
	 * @return NULL if inputed value is NULL. In other cases return Float object
	 */
	public static Float bigDecimalToFloat(BigDecimal value)
	{
		return value == null ? null : value.floatValue();
	}
	
	/**
	 * Get request JSON string from Jqgrid request to build ReleasedHold list 
	 * 
	 * @param request Http request
	 * @return ReleasedHold list
	 * @throws ParseException 
	 */
	public static List<ReleasedHold> convertToReleasedHolds(HttpServletRequest request) throws ParseException  {
		//get JSON array from request
		JSONArray outerArray = (JSONArray) JSONSerializer.toJSON(getJsonDataList(request, ReleasedHoldParameter.JSON_RELEASED_HOLDS));
		JSONObject json = (JSONObject) outerArray.get(0);
		//convert to ReleasedHolds
		return fromJSONtoRelesedHolds(json);
	}
	
	/**
	 * format protocal file:// or \\ or / into file:/// which is supported by  
	 * IE and Mozila
	 * @param linkList external link list
	 * @return formatted link list
	 */
	public static List<Link> formatProtocal(List<Link> linkList) {
		List<Link> formatLinks = new ArrayList<Link>();
		for (Link link :linkList) {
			if (link.getLink().indexOf(FILE_PROTOCAL + FILE_PROTOCAL_SEPARATOR) != 0) {
				//convert file:// into file:///
				if (link.getLink().indexOf(FILE_PROTOCAL) == 0) {
					link.setLink(link.getLink().replaceFirst(FILE_SEPARATOR_PATTERN, FILE_PROTOCAL_SEPARATOR));
				} 
				// convert \\ into file:///
				else if (link.getLink().indexOf(PATH_NET_WINDOW_SEPARATOR) == 0) {
					link.setLink(link.getLink().replaceFirst(PATH_NET_WINDOW_SEPARATOR + PATH_NET_WINDOW_SEPARATOR, FILE_PROTOCAL + FILE_PROTOCAL_SEPARATOR));
				}
				// convert / into file:///
				else if (link.getLink().indexOf(PATH_UNIX_SEPARATOR) == 0) {
					link.setLink(link.getLink().replaceFirst(PATH_UNIX_SEPARATOR, FILE_PROTOCAL + FILE_PROTOCAL_SEPARATOR));
				}
				// convert \ into file:///
				else if (link.getLink().indexOf(PATH_WINDOW_SEPARATOR) == 0) {
					link.setLink(link.getLink().replaceFirst(PATH_WINDOW_SEPARATOR + PATH_WINDOW_SEPARATOR, FILE_PROTOCAL + FILE_PROTOCAL_SEPARATOR));
				}
			}
			formatLinks.add(link);
		}
		
		return formatLinks;
	}
	
	public static String convertFileProtocal(String fileLink) {
		if (fileLink.indexOf(FILE_PROTOCAL + FILE_PROTOCAL_SEPARATOR) == 0) {
			return fileLink.replaceFirst(FILE_PROTOCAL + FILE_PROTOCAL_SEPARATOR, PATH_UNIX_SEPARATOR);
		} 
		return "";
	}
	
	public static boolean isFileProtocal(String link) {
		return link.indexOf(FILE_PROTOCAL) == 0;
	}
	
	/***********PRIVATE METHOD****************/
	
	/**
	 * Convert from the String which represents the month to corresponding value of that month
	 * @param String
	 * @return int
	 */
	private static int getMonthFromString(String month)
	{
		for (int i=0;i<FXFactorConstant.NUM_OF_MONTH;++i)
		{
			if (FXFactorConstant.MONTH_LIST[i].equalsIgnoreCase(month))
			{
				return i+1;
			}
		}
		return -1;
	}
	
	/**
	 * Get JSON list from request
	 * @param HttpServletRequest
	 * @return String[]
	 */
	@SuppressWarnings("unchecked")
	private static String[] getJsonDataList(HttpServletRequest request, String keyName)
	{
		Iterator<Map.Entry<String, String[]>> entries = request.getParameterMap().entrySet().iterator();
		String[] fxTable = {};
		while (entries.hasNext())
		{
			Map.Entry<String, String[]> entry = entries.next();    
			String key = entry.getKey();
			if (key.equalsIgnoreCase(keyName))
			{
				fxTable = entry.getValue();
			}
		}
		return fxTable;
		
	}
	
	/**
	 * Convert from JSONObject to  List<FXFactor>
	 * @param JSONObject 
	 * @return List<FXFactor>
	 */
	private static List<FXFactor> fromJSONtoFXFactor(JSONObject json)
	{
		List<FXFactor> fxFactors = new ArrayList<FXFactor>();
		int i=0;
		JSONObject monthContent = null;
		do
		{
			monthContent =  (JSONObject) json.getJSONObject(String.valueOf(i));
			if (!monthContent.isNullObject())
			{
				FXFactor fxFactor = new FXFactor();
				fxFactor.setFxPlan(((String)monthContent.get(FXFactorConstant.FXPLAN)).isEmpty() ? FXFactorConstant.EMPTY_FX_FACTOR : (float) monthContent.getDouble(FXFactorConstant.FXPLAN));
				fxFactor.setFxRW(((String)monthContent.get(FXFactorConstant.FXRW)).isEmpty() ? FXFactorConstant.EMPTY_FX_FACTOR :(float) monthContent.getDouble(FXFactorConstant.FXRW));
				fxFactor.setFxRWH(((String)monthContent.get(FXFactorConstant.FXRWH)).isEmpty() ? FXFactorConstant.EMPTY_FX_FACTOR :(float) monthContent.getDouble(FXFactorConstant.FXRWH));
				fxFactor.setStatus(monthContent.getInt(FXFactorConstant.STATUS));
				
				boolean processing = (
						fxFactor.getFxPlan() == FXFactorConstant.EMPTY_FX_FACTOR &&
						fxFactor.getFxRW() == FXFactorConstant.EMPTY_FX_FACTOR &&
						fxFactor.getFxRWH() == FXFactorConstant.EMPTY_FX_FACTOR);

				if (!(processing && fxFactor.getStatus()==FXFactorConstant.UNMANAGED))
				{
					fxFactor.setMonth(getMonthFromString((String) monthContent.get(FXFactorConstant.MONTH)));
					fxFactor.setComment((String) monthContent.get(FXFactorConstant.COMMENT));
					fxFactor.setPriority(monthContent.getInt(FXFactorConstant.PRIORITY));
					fxFactor.setProdArea(monthContent.getInt(FXFactorConstant.PRODAREA));
					fxFactor.setYear(monthContent.getInt(FXFactorConstant.YEAR));
					fxFactor.setId(fxFactor.getMonth());
					
					fxFactors.add(fxFactor);
				}
				++i;
			}
			
		} while (!monthContent.isNullObject());
		return fxFactors;
	}
	
	/**
	 * Convert from JSONObject to  List<Link>
	 * @param JSONObject 
	 * @return List<Link>
	 */
	private static List<Link> fromJSONtoLink(JSONObject json)
	{
		List<Link> links = new ArrayList<Link>();
		int i=0;
		JSONObject linkContent = null;
		do
		{
			linkContent =  (JSONObject) json.getJSONObject(String.valueOf(i));
			if (!linkContent.isNullObject())
			{
				Link link = new Link();
				link.setId(Integer.valueOf(linkContent.getString(ModifiedLinksContanst.ID)));
				link.setLink(linkContent.getString(ModifiedLinksContanst.LINK));
				link.setTitle(linkContent.getString(ModifiedLinksContanst.TITLE));
				link.setOrder(Integer.valueOf(linkContent.getString(ModifiedLinksContanst.ORDER)));
				link.setStatus(Integer.valueOf(linkContent.getString(ModifiedLinksContanst.STATUS)));
				
				if (!(link.getStatus() == ModifiedLinksContanst.DELETED_LINK_STATUS && link.getId()<0)&&
						!(link.getStatus() == ModifiedLinksContanst.NEW_LINK_STATUS && link.getLink().isEmpty() && link.getTitle().isEmpty())
					)
				{
					links.add(link);
				}
				++i;
			}
			
		} while (!linkContent.isNullObject());
		return links;
	}
	
	/**
	 * Convert from JSONObject to  ReleasedHold List
	 * @param JSONObject 
	 * @return List<ReleasedHold>
	 * @throws ParseException 
	 */
	private static List<ReleasedHold> fromJSONtoRelesedHolds(JSONObject json) throws ParseException
	{
		List<ReleasedHold> rHList = new ArrayList<ReleasedHold>();
		int i=0;
		JSONObject rHObject = null;
		
		// convert from string to date
	    SimpleDateFormat sdf = new SimpleDateFormat(CommonConstants.FULL_DATE_FORMAT);
	    
		do
		{
			rHObject =  (JSONObject) json.getJSONObject(String.valueOf(i));
			if (!rHObject.isNullObject())
			{
				ReleasedHold rH = new ReleasedHold();
				rH.setLotId(rHObject.getString(ReleasedHoldColumnName.LOT_ID_DB));
				rH.setRecipeId(rHObject.getString(ReleasedHoldColumnName.RECIPE_ID_DB));
				rH.setStage(rHObject.getString(ReleasedHoldColumnName.STAGE_DB));
				rH.setHaltStart(sdf.parse(rHObject.getString(ReleasedHoldColumnName.HALT_START_DB)));
				rH.setHaltEnd(sdf.parse(rHObject.getString(ReleasedHoldColumnName.HALT_END_DB)));
				
				rH.setHoldCodeReclassified(rHObject.getString(ReleasedHoldColumnName.HOLD_CODE_RECLASSIFIED_DB));
				rH.setHoldCommentReclassified(rHObject.getString(ReleasedHoldColumnName.HOLD_COMMENT_RECLASSIFIED_DB));
					
				rHList.add(rH);
				
				++i;
			}
			
		} while (!rHObject.isNullObject());
		return rHList;
	}
	
	/**
	 * Add new FXFactor object to fxFactors at pos position 
	 * @param fxFactors
	 * @param condition4FxFactor
	 * @param pos
	 * @param month
	 */
	private static void addFxFactor(List<FXFactor> fxFactors, Condition4FxFactor condition4FxFactor, int pos, int month)
	{
		FXFactor fxFactor = new FXFactor();
		fxFactor.setMonth(month);
		fxFactor.setPriority(condition4FxFactor.getPriority());
		fxFactor.setProdArea(condition4FxFactor.getProdArea());
		fxFactor.setYear(condition4FxFactor.getYear());
		fxFactor.setStatus(FXFactorConstant.UNMANAGED);
		
		fxFactor.setFxPlan(FXFactorConstant.EMPTY_FX_FACTOR);
		fxFactor.setFxRW(FXFactorConstant.EMPTY_FX_FACTOR);
		fxFactor.setFxRWH(FXFactorConstant.EMPTY_FX_FACTOR);
		
		fxFactors.add(pos,fxFactor);	
	}
	
	
}
